<!--
 * @Author: wangshuixin 2412658453@qq.com
 * @Date: 2024-11-15 17:28:53
 * @LastEditors: wangshuixin 2412658453@qq.com
 * @LastEditTime: 2025-01-14 15:58:25
 * @FilePath: \sr_system_manager_ui\src\components\SheetInfo\index.vue
 * @Description: 表单组件
-->
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { SheetInfoComponent } from '@/types/component'

const props = defineProps({
  /* 传入的组件 */
  components: {
    type: Array as () => SheetInfoComponent[],
    default: []
  },
  /* 表单section标题 */
  sectionTitle: {
    type: String,
    default: '成果信息'
  },
  /* 单行显示 */
  singleRow: {
    type: Boolean,
    default: true
  }
})

const emits = defineEmits(['editSuccess'])

// 表单对象
const sheetForm = ref()
// 搜索条件的参数
const sheetInfoRef = ref({
  params: reactive({}) // 使用reactive创建一个响应式对象
});

// 表单校验
const checkSheetFormValidate = (callback: Function) => {
  const allMsg = sheetForm.value.map((sheetItem: any) => {
    return sheetItem.validate()
  })
  Promise.all(allMsg.map(p => p.catch(err => ({ isRejected: true, reason: err }))))
  .then(results => {
    // 过滤出所有被拒绝的 Promise
    const rejectedPromises = results.filter(result => result.isRejected).length > 0 ? results.filter(result => result.isRejected) : undefined;
    // 处理被拒绝的 Promise
    if (callback) {
      callback(rejectedPromises)
    }
  })
  .catch(err => {
    // 处理任何其他错误
    console.error('An error occurred:', err);
  });
}

// 取消表单校验
const cancelSheetFormValidate = (sheetKey?: string) => {
  if (sheetKey) {
    props.components.forEach((component: SheetInfoComponent, index: number) => {
      if (component.key === sheetKey) {
        sheetForm.value[index].clearValidate()
      }
    })
  } else {
    sheetForm.value.map((sheetItem: any) => {
      sheetItem.clearValidate()
    })
  }
}

// 可能存在依赖的组件的值，进行了修改值的操作，此处进行对应依赖组件的重置操作
const resetDependedComponentValue = (key: string) => {
  props.components.map((component: SheetInfoComponent) => {
    // 如果本组件是依赖于key的组件，则需要将本组件对应参数重置
    if (component.depended === key) {
      sheetInfoRef.value.params[component.key] = undefined
      // 重置所有依赖线上的组件
      resetDependedComponentValue(component.key)
    }
    if (component.type === 99 && component.config && component.config.list) {
      component.config.list.map((subComponent: SheetInfoComponent) => {
        if (subComponent.depended === key) {
          sheetInfoRef.value.params[subComponent.key] = undefined
          // 重置所有依赖线上的组件
          resetDependedComponentValue(subComponent.key)
        }
      })
    }
  })
}

// 快速填写
const quickWrite = (key: string, text: string|undefined) => {
  if (!sheetInfoRef.value.params[key]) {
    sheetInfoRef.value.params[key] = text
  } else {
    sheetInfoRef.value.params[key] += text
  }
}

// 单行表单校验样式
const formRowStyle = (component: SheetInfoComponent) => {
  // 基础样式
  const baseStyle = {
    '--sheetInfoLabelPaddingLeft': component.config && component.config.readonly ? '12px' : '0px',
    '--sheetInfoLabelTextAlign': component.config && component.config.readonly ? 'left' : 'right',
    '--sheetInfoLabelAlignItems': component.config && component.config.readonly ? 'center' : 'flex-start',
    '--sheetInfoLabelLineHeight': component.config && component.config.sheetInfoLabelLineHeight ? component.config.sheetInfoLabelLineHeight : (component.config && component.config.readonly ? '22px' : '32px'),
    '--sheetInfoLabelHeight': component.config && component.config.sheetInfoLabelHeight ? component.config.sheetInfoLabelHeight : (component.config && component.config.readonly ? 'auto' : '32px'),
    '--sheetInfoRowMarginTop': component.config && component.config.sheetInfoRowMarginTop ? component.config.sheetInfoRowMarginTop : (component.config && component.config.readonly ? '0px' : '20px'),
    '--sheetInfoRowLineHeight': component.config && component.config.sheetInfoRowLineHeight ? component.config.sheetInfoRowLineHeight : (component.config && component.config.readonly ? '22px' : '32px'),
    width: `calc(${component.labelWidth ?? '120px'} + ${component.width ?? '50% - 120px'})`,
    display: !component.hidden ? (component.config && component.config.readonly ? 'flex' : 'inline-block') : 'none'
  }
  if (component.config && component.config.readonly && component.config.previewStyle && !component.hidden) {
    return { ...baseStyle, ...component.config.previewStyle }
  } else if (component.config && !component.config.readonly && component.config.editStyle && !component.hidden) {
    return { ...baseStyle, ...component.config.editStyle }
  }
  return baseStyle
}

// 描述信息 样式
const descStyle = (component: SheetInfoComponent) => {
  // 基础样式
  const baseStyle = {
    lineHeight: '20px',
    color: '#86909C',
    fontSize: '12px',
  }
  if (component.config && !component.config.readonly && component.config.descStyle && !component.hidden) {
    return { ...baseStyle, ...component.config.descStyle }
  }
  return baseStyle
}

watch(
  () => props.components,
  () => {
    if (props.components.length > 0) {
      props.components.map((component: SheetInfoComponent) => {
        if (component.key) {
          sheetInfoRef.value.params[component.key] = component.cValue
        }
        if (component.type === 99 && component.config && component.config.list) {
          component.config.list.map((subComponent: SheetInfoComponent) => {
            if (subComponent.key) {
              sheetInfoRef.value.params[subComponent.key] = subComponent.cValue ?? undefined
            }
          })
        }
      })
    }
  }, {
    immediate: true,
    deep: true
  }
)

defineExpose({
  checkSheetFormValidate, // 表单验证
  cancelSheetFormValidate, // 取消表单验证
  params: sheetInfoRef.value.params
})
</script>

<template>
  <div class="sheet-info-area">
    <div class="area-title">{{ props.sectionTitle }}</div>
    <div :class="props.singleRow ? 'singleRowClass' : 'multiRowsClass'">
      <el-form
        ref="sheetForm"
        :model="sheetInfoRef.params"
        v-for="component in props.components"
        :key="component.key"
        size="default"
        :style="formRowStyle(component)"
      >
        <el-form-item
          v-if="!component.hidden"
          :label="component.title"
          :label-position="component.labelPosition ?? (component.config && component.config.readonly ? 'left' : 'right')"
          :prop="component.key"
          :label-width="component.labelWidth ?? '120px'"
          :rules="component.config ? component.config.rules : []"
        >
          <!-- 纯文本 -->
          <div v-if="component.type === 3" :style="component.config && component.config.rowTextStyle ? component.config.rowTextStyle : {}">
            <slot :name="component.key" :component="component">
              {{ component.cValue }}{{ component.config && component.config.suffix ? component.config.suffix : '' }}
            </slot>
          </div>
          <!-- input输入框 -->
          <el-input
            v-if="component.type === 0 && (!component.config || (component.config && !component.config.readonly))"
            :type="(component.config && component.config.type) ? component.config.type : 'text'"
            v-model="sheetInfoRef.params[component.key]"
            :maxlength="component.config?.maxLength"
            :placeholder="component.placeholder ?? '请输入'"
            v-bind="component.config"
            @change="resetDependedComponentValue(component.key)"
          />
          <div v-else-if="component.type === 0 && component.config && component.config.readonly" :style="component.config && component.config.rowTextStyle ? component.config.rowTextStyle : {}">
            <slot :name="component.key" :component="component">
              {{ component.cValue }}
            </slot>
          </div>
          <div v-if="component.type === 0 && (component.config && !component.config.readonly && component.config.type === 'textarea')" class="quickTextRow">
            <div v-for="item in component.config.options" :key="item.value" class="quickItem" @click="quickWrite(component.key, item.title)">
              {{ item.title }}
            </div>
          </div>
          <!-- input-number输入框 -->
          <el-input-number
            v-if="component.type === -1 && (!component.config || (component.config && !component.config.readonly))"
            v-model="sheetInfoRef.params[component.key]"
            :placeholder="component.placeholder ?? '请输入'"
            v-bind="component.config"
            @change="resetDependedComponentValue(component.key)"
          >
            <template v-if="component.config && component.config.suffix" #suffix>
              {{ component.config.suffix }}
            </template>
          </el-input-number>
          <div v-else-if="component.type === -1 && component.config && component.config.readonly" :style="component.config && component.config.rowTextStyle ? component.config.rowTextStyle : {}">
            <slot :name="component.key" :component="component">
              {{ component.cValue }}
            </slot>
          </div>
          <!-- 下拉选择框 -->
          <el-select
            v-if="component.type === 1 && component.config && !component.config.readonly"
            v-model="sheetInfoRef.params[component.key]"
            :placeholder="component.placeholder ?? '请选择'"
            v-bind="component.config"
            @change="resetDependedComponentValue(component.key)"
          >
            <el-option
              v-for="option in component.config.options"
              :key="option.value"
              :label="option.title"
              :value="option.value">
            </el-option>
          </el-select>
          <div v-else-if="component.type === 1 && component.config && component.config.readonly" :style="component.config && component.config.rowTextStyle ? component.config.rowTextStyle : {}">
            <slot :name="component.key" :component="component">
              <span
                v-if="component.config.options && component.config.options.filter(option => option.value === sheetInfoRef.params[component.key]).length > 0"
              >
                {{ component.config.options.filter(option => option.value === sheetInfoRef.params[component.key])[0].title }}
              </span>
              <span v-else>{{ component.cValue }}</span>
            </slot>
          </div>
          <!-- 日期选择器 -->
          <el-date-picker
            v-if="component.type === 2 && (!component.config || (component.config && !component.config.readonly))"
            v-model="sheetInfoRef.params[component.key]"
            :type="(component.config && component.config.type) ? component.config.type : 'date'"
            :placeholder="component.placeholder ?? '请选择日期'"
            v-bind="component.config"
            :value-format="(component.config && component.config.valueFormat) ? component.config.valueFormat : 'YYYY-MM-DD'"
            @change="resetDependedComponentValue(component.key)"
          >
          </el-date-picker>
          <div v-else-if="component.type === 2 && component.config && component.config.readonly" :style="component.config && component.config.rowTextStyle ? component.config.rowTextStyle : {}">
            <slot :name="component.key" :component="component">
              {{ component.cValue }}
            </slot>
          </div>
          <!-- 单选选择器 -->
          <div v-if="component.type === 4 && component.config && !component.config.readonly" class="component-row">
            <div v-for="(radio, index) in component.config.options" :key="index">
              <el-radio
                v-model="sheetInfoRef.params[component.key]"
                :label="radio.value"
                :disabled="component.config.readonly"
                v-bind="component.config"
                :style="{marginLeft: index !== 0 ? '24px' : '0px' }"
                @change="resetDependedComponentValue(component.key)"
              >
                  {{ radio.title }}
              </el-radio>
            </div>
          </div>
          <div v-else-if="component.type === 4 && component.config && component.config.readonly && component.config.options && component.config.options.filter(radio => radio.value === sheetInfoRef.params[component.key]).length > 0">
            <slot :name="component.key" :component="component">
              {{ component.config.options.filter(radio => radio.value === sheetInfoRef.params[component.key])[0].title }}
            </slot>
          </div>
          <!-- 附件 -->
          <FileUpload
            v-if="component.type === 5 && (!component.config || (component.config && !component.config.readonly))"
            v-model="sheetInfoRef.params[component.key]"
            :isShowTip="component.config && component.config.showTip ? true : false"
            :specificRequirement="component.config && component.config.specificRequirement ? component.config.specificRequirement : undefined"
          />
          <div v-if="component.type === 5 && component.config && component.config.readonly">
            <slot :name="component.key" :component="component">
              <div v-for="(file, index) in component.cValue" :key="index">
                <FilePreviewItem :file="file"/>
              </div>
            </slot>
          </div>
          <!-- 插槽 -->
          <div v-if="component.type === 6">
            <slot :name="component.key" :component="component" />
          </div>
          <!-- 多混合组件 -->
          <div v-if="component.type === 99 && component.config && component.config.list" :class="{ 'mixed-component-row': !component.config || (component.config && !component.config.readonly), 'mixed-component-col': component.config && component.config.readonly}">
            <div
              v-for="(subComponent, index) in component.config.list"
              :key="index"
              :style="{
                marginLeft: index !== 0 && !subComponent.hidden && (!component.config || (component.config && !component.config.readonly)) ? '20px' : '0px',
                marginTop: index !== 0 && !subComponent.hidden && subComponent.config && subComponent.config.readonly && (subComponent.depended ? !!sheetInfoRef.params[subComponent.depended] : true) ? '12px' : '0px'
              }"
            >
              <el-form-item
                v-if="!subComponent.hidden && (!subComponent.config || (subComponent.config && !subComponent.config.readonly)) && (subComponent.depended ? !!sheetInfoRef.params[subComponent.depended] : true)"
                :prop="subComponent.key"
                :label-width="subComponent.labelWidth ?? '0px'"
                :rules="subComponent.config ? subComponent.config.rules : []"
              >
                <!-- 纯文本 -->
                <div v-if="subComponent.type === 3 && subComponent.config && !subComponent.config.readonly">
                  {{ sheetInfoRef.params[subComponent.key] }}
                  {{ subComponent.config && subComponent.config.suffix ? subComponent.config.suffix : '' }}
                </div>
                <!-- input-number输入框 -->
                <el-input-number
                  v-if="subComponent.type === -1 && (!subComponent.config || (subComponent.config && !subComponent.config.readonly)) && (subComponent.depended ? !!sheetInfoRef.params[subComponent.depended] : true)"
                  v-model="sheetInfoRef.params[subComponent.key]"
                  :placeholder="subComponent.placeholder ?? '请输入'"
                  v-bind="subComponent.config"
                  :style="{ width: subComponent.width ?? '150px' }"
                  @change="resetDependedComponentValue(subComponent.key)"
                >
                  <template v-if="subComponent.config && subComponent.config.suffix" #suffix>
                    {{ subComponent.config.suffix }}
                  </template>
                </el-input-number>
                <!-- 下拉选择框 -->
                <el-select
                  v-if="subComponent.type === 1 && subComponent.config && !subComponent.config.readonly"
                  v-model="sheetInfoRef.params[subComponent.key]"
                  :disabled="subComponent.depended ? !sheetInfoRef.params[subComponent.depended] : false"
                  :placeholder="subComponent.placeholder ?? '请选择'"
                  v-bind="subComponent.config"
                  :style="{ width: subComponent.width ?? '150px' }"
                  @change="resetDependedComponentValue(subComponent.key)"
                >
                  <el-option
                    v-for="option in subComponent.config.options"
                    :key="option.value"
                    :label="option.title"
                    :value="option.value">
                  </el-option>
                </el-select>
              </el-form-item>
              <div v-else-if="!subComponent.hidden && subComponent.config && subComponent.config.readonly && (subComponent.depended ? !!sheetInfoRef.params[subComponent.depended] : true)" style="flex-direction: row;">
                {{ `${subComponent.title}：` }}
                <span v-if="subComponent.type === 1 && subComponent.config && subComponent.config.options && subComponent.config.options.filter(option => option.value === sheetInfoRef.params[subComponent.key]).length > 0">
                  {{ subComponent.config.options.filter(option => option.value === sheetInfoRef.params[subComponent.key])[0].title }}
                </span>
                <span v-else>
                  {{ sheetInfoRef.params[subComponent.key] }}
                </span>
                {{ subComponent.config && subComponent.config.suffix ? subComponent.config.suffix : '' }}
              </div>
            </div>
          </div>
          <div v-if="component.config && component.config.desc && !component.config.readonly && component.type !== 2" class="descStyle" :style="descStyle(component)">{{ component.config.desc }}</div>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<style scoped lang="scss">
.sheet-info-area {
  display: flex;
  flex-direction: column;
  height: auto;
  background-color: white;
  .singleRowClass {
    display: flex;
    flex-direction: column;
  }
  .multiRowsClass {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
  }
  .area-title {
    height: 20px;
    font-weight: bold;
    color: #1D2129;
    font-size: 14px;
    margin-top: 24px;
    margin-bottom: 16px;
    margin-left: 0px;
  }
  // 快捷输入
  .quickTextRow {
    display: flex;
    flex-wrap: wrap;
    margin-top: 8px;
    gap: 12px;
    color: #86909C;
  }
  .quickItem {
    border-radius: 4px;
    background-color: #F2F3F5;
    color: #1D2129;
    padding: 4px 16px;
    line-height: 20px;
  }
  .quickItem:hover {
    background-color: #E8F7FF;
    color: #57A9FB;
  }
  .component-row {
    display: flex;
    flex-direction: row;
    :deep(.el-radio) {
      height: var(--sheetInfoRowLineHeight);
    }
  }
  .mixed-component-col {
    display: flex;
    flex: 1;
    flex-direction: column;
    line-height: 22px;
    padding: 16px;
    background-color: #F7F8FA;
    border-radius: 4px;
    :deep(.el-form-item) {
      margin-top: 0px;
    }
  }
  .mixed-component-row {
    display: flex;
    flex-direction: row;
    :deep(.el-form-item) {
      margin-top: 0px;
    }
  }
  :deep(.el-form-item) {
    margin-top: var(--sheetInfoRowMarginTop);
    margin-bottom: 0px;
    margin-right: 0px;
  }
  :deep(.el-form-item__content) {
    line-height: var(--sheetInfoRowLineHeight);
  }
  /* input-number输入框 */
  :deep(.el-input-number) {
    width: 100%;
  }
  :deep(.el-input-number .el-input__inner) {
    text-align: left;
  }
  /* 修改form的title */
  :deep(.el-form .el-form-item__label) {
    font-weight: normal;
    color: #4E5969;
    padding-left: var(--sheetInfoLabelPaddingLeft);
    align-items: var(--sheetInfoLabelAlignItems);
    text-align: var(--sheetInfoLabelTextAlign);
    line-height: var(--sheetInfoLabelLineHeight);
    height: var(--sheetInfoLabelHeight);
  }
  /* 日期选择器 */
  :deep(.el-date-editor.el-input, .el-date-editor.el-input__wrapper) {
    flex: 1;
  }
  /* textarea */
  :deep(.el-textarea__inner) {
    height: 200px;
  }
  /* 附件 - 上传按钮 */
  :deep(.el-upload--picture-card) {
    height: 80px;
    width: 80px;
  }
  /* 附件 - 预览按钮 */
  :deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
    height: 80px;
    width: 80px;
  }
  :deep(.el-upload-list--picture-card .el-upload-list__item-thumbnail) {
    height: 80px;
    width: 80px;
  }
  :deep(.el-upload-list--picture-card .el-upload-list__item) {
    height: 80px;
    width: 80px;
  }
}
</style>